#include "HardwareResourceManager.h"
#include "D3D10RenderDevice.h"
#include "Utility/Utility.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"
#include "EffectTechnique.h"

namespace HWResourceManagerInternal
{
	static bool        IsDynamic(EHardwareBufferUsage eUsage)
	{
		switch(eUsage)
		{
		case HBU_DYNAMIC:
		case HBU_DYNAMIC_WRITE_ONLY:
		case HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE:
			return true;
		}
		return false;
	}

	static D3D10_USAGE TranslateToD3D10Usage(EHardwareBufferUsage eUsage)
	{
		if (IsDynamic(eUsage))
		{
			return D3D10_USAGE_DYNAMIC;
		}
		else
		{
			return D3D10_USAGE_DEFAULT;
		}
	}

	static UINT32 GetCPUAccessFlags(EHardwareBufferUsage eUsage)
	{
		if (IsDynamic(eUsage))
		{
			return D3D10_CPU_ACCESS_WRITE;
		}
		return 0;
	}
}

CHardwareResourceManager::CHardwareResourceManager(CD3D10RenderDevice* pDevice)
	: m_pDevice(pDevice)
	, m_dwVertexBuffer_IDGen(0)
	, m_dwIndexBuffer_IDGen(0)
	, m_dwEffectTech_IDGen(0)
{

}

CHardwareResourceManager::~CHardwareResourceManager()
{	
}

CVertexBuffer* 
CHardwareResourceManager::CreateVertexBuffer( EHardwareBufferUsage eUsage, UINT32 dwVertexSize, UINT32 dwVertexCount)
{
	ID3D10Buffer* pD3DBuffer = 0;

	D3D10_BUFFER_DESC vbd;

	vbd.Usage          = HWResourceManagerInternal::TranslateToD3D10Usage(eUsage);
	vbd.ByteWidth      = dwVertexCount * dwVertexSize;
	vbd.BindFlags      = D3D10_BIND_VERTEX_BUFFER;
	vbd.CPUAccessFlags = HWResourceManagerInternal::GetCPUAccessFlags(eUsage);
	vbd.MiscFlags      = 0;

	HR(m_pDevice->GetDevice()->CreateBuffer(&vbd, NULL, &pD3DBuffer));	
	if (pD3DBuffer)
	{
		UINT32 dwID = ++m_dwVertexBuffer_IDGen;
		CVertexBuffer* pVertexBuffer = new CVertexBuffer(eUsage, pD3DBuffer, RESOURSE_VERTEX_BUFFER, dwVertexCount, dwVertexSize);
		m_VertexBuffers.insert(pVertexBuffer);
		return pVertexBuffer;
	}
	return NULL;
}

CIndexBuffer* 
CHardwareResourceManager::CreateIndexBuffer( EHardwareBufferUsage eUsage, UINT32 dwIndexNum)
{
	ID3D10Buffer* pD3DBuffer = 0;

	D3D10_BUFFER_DESC vbd;

	vbd.Usage          = HWResourceManagerInternal::TranslateToD3D10Usage(eUsage);
	vbd.ByteWidth      = dwIndexNum * sizeof(UINT32);
	vbd.BindFlags      = D3D10_BIND_INDEX_BUFFER;
	vbd.CPUAccessFlags = HWResourceManagerInternal::GetCPUAccessFlags(eUsage);
	vbd.MiscFlags      = 0;

	HR(m_pDevice->GetDevice()->CreateBuffer(&vbd, NULL, &pD3DBuffer));

	if (pD3DBuffer)
	{
		UINT32 dwID = ++m_dwIndexBuffer_IDGen;

		CIndexBuffer* pIndexBuffer = new CIndexBuffer(dwID, pD3DBuffer, RESOURCE_INDEX_BUFFER, dwIndexNum);
		m_IndexBuffers.insert(pIndexBuffer);
		return pIndexBuffer;
	}
	return NULL;
}

CD3DEffect* 
CHardwareResourceManager::CreateEffect( const std::wstring& strEffectPath )
{
	ID3D10Device* pD3DDevice = m_pDevice->GetDevice();

	DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined(DEBUG) || defined(_DEBUG)
	dwShaderFlags |= D3D10_SHADER_DEBUG;
	dwShaderFlags |= D3D10_SHADER_SKIP_OPTIMIZATION;
#endif

	ID3D10Effect* pEffect;

	ID3DBlob* pCompileErrors = 0;
	HRESULT hr = 0;
	hr = D3DX10CreateEffectFromFile(strEffectPath.c_str(), 0, 0, "fx_4_0", dwShaderFlags, 0, 
		pD3DDevice, 0, 0, &pEffect, &pCompileErrors, 0);

	if (FAILED(hr))
	{
		if (pCompileErrors)
		{
			MessageBoxA(0, (char*)pCompileErrors->GetBufferPointer(), 0, 0);
			SAFE_RELEASE(pCompileErrors);
		}
		DXTrace(__FILE__, (DWORD)__LINE__, hr, L"D3DX10CreateEffectFromFile", true);
	}

	if (pEffect)
	{
		UINT32 dwID = ++m_dwEffectTech_IDGen;
		CD3DEffect* pD3DEffect = new CD3DEffect(dwID, pEffect, RESOURCE_EFFECT);
		return pD3DEffect;
	}
	return NULL;
}


void CHardwareResourceManager::ReleaseResource( CHardwareResource* pResource )
{
	DBG_ASSERT(pResource != NULL);

	if (pResource->m_eResType == RESOURSE_VERTEX_BUFFER)
	{
		CVertexBuffer* pVertexBuf = dynamic_cast<CVertexBuffer*>(pResource);
		m_VertexBuffers.erase(pVertexBuf);
	}
	else if (pResource->m_eResType == RESOURCE_INDEX_BUFFER)
	{
		CIndexBuffer* pIndexBuf = dynamic_cast<CIndexBuffer*>(pResource);
		m_IndexBuffers.erase(pIndexBuf);
	} 
	else if (pResource->m_eResType == RESOURCE_EFFECT)
	{
		CD3DEffect* pEffect = dynamic_cast<CD3DEffect*>(pResource);
		m_EffectTechs.erase(pEffect);
	}

	pResource->Release();
	SAFE_DELETE(pResource);
}
